package net.alche.fa.core.doing;
import net.alche.fa.core.model.Logic;
import net.alche.fa.core.model.FaValue;
import net.alche.fa.core.CodeLevel;
import net.alche.fa.core.SageStone;
import net.alche.fa.core.FaException;
import net.alche.al.util.AC;

//
// faValue / var
//
@FaAnnoDoing
public class MathDo extends AbstractDoing {

  @Override
  public boolean matched(String what, Logic g) {
    return what != null&& ("+".equals(what) || "-".equals(what) || "*".equals(what) || "/".equals(what) || "%".equals(what));
  }

  @Override
  public FaValue doing(Logic g, CodeLevel lvl, SageStone st) throws FaException {
    FaValue objVal = super.getLogicObjVal(st, lvl, g);
    FaValue dataVal = super.getLogicDataVal(st, lvl, g);
    return doMath(objVal, g.getWhat(), dataVal);
  }

  public boolean compareWhat(String what1, String what2) {
    if (what2 == null) {
      return true;
    }
    if ("*".equals(what1) || "/".equals(what1) || "%".equals(what1)) {
      return true;
    }
    else if ( "+".equals(what1) || "-".equals(what1) && ("+".equals(what2) || "-".equals(what2))) {
      return true;
    }
    return false;
  }

  public static FaValue doMath(FaValue v1, String what, FaValue v2) throws FaException {
    if (what == null) {
      AC.log("...");
    }
    switch (what) {
      case "+" : {
        return plus(v1, v2);
      }
      case "-" : {
        return subtraction(v1, v2);
      }
      case "*" : {
        return multiPlication(v1, v2);
      }
      case "/" : {
        return division(v1, v2);
      }
      case "%" : {
        return mod(v1, v2);
      }
    }
    return v1;
  }

  private static char getDataType(FaValue v1, FaValue v2) {
    if (isType(v1, v2, FaValue._STRING)) {
      return FaValue._STRING;
    }
    else if ( isType(v1, v2, FaValue._DOUBLE)) {
      return FaValue._DOUBLE;
    }
    else if ( isType(v1, v2, FaValue._LONG)) {
      return FaValue._LONG;
    }
    else if ( isType(v1, v2, FaValue._BYTE)) {
      return FaValue._INT;
    }
    else if ( isType(v1, v2, FaValue._CHAR)) {
      return FaValue._INT;
    }
    else if ( isType(v1, v2, FaValue._BOOL)) {
      return FaValue._BOOL;
    }
    else if ( isType(v1, v2, FaValue._INT)) {
      return FaValue._INT;
    }
    else {
      return FaValue._STRING;
    }
  }

  private static boolean isType(FaValue v1, FaValue v2, char type) {
    if (v1.getType() == type || v2.getType() == type) {
      return true;
    }
    return false;
  }

  private static FaValue plus(FaValue v1, FaValue v2) throws FaException {
    if (v1 == null || v2 == null) {
      throw new FaException(FaException.NULL_ERROR, AC.json.toStr(v1) + " + " + AC.json.toStr(v2));
    }
    char dataType = getDataType(v1, v2);
    if (dataType == FaValue._INT) {
      if (v1.isNull() || v2.isNull()) {
        throw new FaException(FaException.NULL_ERROR, v1.getJava() + " + " + v2.getJava());
      }
      return new FaValue(v1.getIntValue() + v2.getIntValue());
    }
    else if ( dataType == FaValue._DOUBLE) {
      return new FaValue(v1.getDoubleValue() + v2.getDoubleValue());
    }
    else if ( dataType == FaValue._LONG) {
      return new FaValue(v1.getLongValue() + v2.getLongValue());
    }
    else {
      return new FaValue(v1.getStrValue() + (v2.isNull() ? "null" : v2.getStrValue()));
    }
  }

  private static FaValue subtraction(FaValue v1, FaValue v2) throws FaException {
    char dataType = getDataType(v1, v2);
    if (dataType == FaValue._INT) {
      return new FaValue(v1.getIntValue() - v2.getIntValue());
    }
    else if ( dataType == FaValue._DOUBLE) {
      return new FaValue(v1.getDoubleValue() - v2.getDoubleValue());
    }
    else if ( dataType == FaValue._LONG) {
      return new FaValue(v1.getLongValue() - v2.getLongValue());
    }
    else {
      throw new FaException(FaException.CODE_TYPE_ERROR, AC.json.toStr(v1.getJava()) + " * " + AC.json.toStr(v2.getJava()));
    }
  }

  private static FaValue multiPlication(FaValue v1, FaValue v2) throws FaException {
    char dataType = getDataType(v1, v2);
    if (dataType == FaValue._INT) {
      return new FaValue(v1.getIntValue() * v2.getIntValue());
    }
    else if ( dataType == FaValue._DOUBLE) {
      return new FaValue(v1.getDoubleValue() * v2.getDoubleValue());
    }
    else if ( dataType == FaValue._LONG) {
      return new FaValue(v1.getLongValue() * v2.getLongValue());
    }
    else {
      throw new FaException(FaException.CODE_TYPE_ERROR, AC.json.toStr(v1.getJava()) + " * " + AC.json.toStr(v2.getJava()));
    }
  }

  private static FaValue division(FaValue v1, FaValue v2) throws FaException {
    char dataType = getDataType(v1, v2);
    if (dataType == FaValue._INT) {
      return new FaValue(v1.getIntValue() / v2.getIntValue());
    }
    else if ( dataType == FaValue._DOUBLE) {
      return new FaValue(v1.getDoubleValue() / v2.getDoubleValue());
    }
    else if ( dataType == FaValue._LONG) {
      return new FaValue(v1.getLongValue() / v2.getLongValue());
    }
    else {
      throw new FaException(FaException.CODE_TYPE_ERROR, AC.json.toStr(v1.getJava()) + " * " + AC.json.toStr(v2.getJava()));
    }
  }

  private static FaValue mod(FaValue v1, FaValue v2) throws FaException {
    char dataType = getDataType(v1, v2);
    if (dataType == FaValue._INT) {
      return new FaValue(v1.getIntValue() % v2.getIntValue());
    }
    else if ( dataType == FaValue._DOUBLE) {
      return new FaValue(v1.getDoubleValue() % v2.getDoubleValue());
    }
    else if ( dataType == FaValue._LONG) {
      return new FaValue(v1.getLongValue() % v2.getLongValue());
    }
    else {
      throw new FaException(FaException.CODE_TYPE_ERROR, AC.json.toStr(v1.getJava()) + " * " + AC.json.toStr(v2.getJava()));
    }
  }
}